package cn.yulr.v4l2multicamera;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.view.SurfaceView;

import java.util.concurrent.TimeUnit;

public class V4L2Camera {

    public static class ImageFormat {

        public static final int V4L2_PIX_FMT_RGB332 = 826427218;
        public static final int V4L2_PIX_FMT_RGB444 = 875836498;
        public static final int V4L2_PIX_FMT_ARGB444 = 842093121;
        public static final int V4L2_PIX_FMT_XRGB444 = 842093144;
        public static final int V4L2_PIX_FMT_RGB555 = 1329743698;
        public static final int V4L2_PIX_FMT_ARGB555 = 892424769;
        public static final int V4L2_PIX_FMT_XRGB555 = 892424792;
        public static final int V4L2_PIX_FMT_RGB565 = 1346520914;
        public static final int V4L2_PIX_FMT_RGB555X = 1363298130;
        public static final int V4L2_PIX_FMT_ARGB555X = -1255058879;
        public static final int V4L2_PIX_FMT_XRGB555X = -1255058856;
        public static final int V4L2_PIX_FMT_RGB565X = 1380075346;
        public static final int V4L2_PIX_FMT_BGR666 = 1213351746;
        public static final int V4L2_PIX_FMT_BGR24 = 861030210;
        public static final int V4L2_PIX_FMT_RGB24 = 859981650;
        public static final int V4L2_PIX_FMT_BGR32 = 877807426;
        public static final int V4L2_PIX_FMT_ABGR32 = 875713089;
        public static final int V4L2_PIX_FMT_XBGR32 = 875713112;
        public static final int V4L2_PIX_FMT_RGB32 = 876758866;
        public static final int V4L2_PIX_FMT_ARGB32 = 875708738;
        public static final int V4L2_PIX_FMT_XRGB32 = 875714626;
        public static final int V4L2_PIX_FMT_GREY = 1497715271;
        public static final int V4L2_PIX_FMT_Y4 = 540291161;
        public static final int V4L2_PIX_FMT_Y6 = 540422233;
        public static final int V4L2_PIX_FMT_Y10 = 540029273;
        public static final int V4L2_PIX_FMT_Y12 = 540160345;
        public static final int V4L2_PIX_FMT_Y16 = 540422489;
        public static final int V4L2_PIX_FMT_Y16_BE = -1607061159;
        public static final int V4L2_PIX_FMT_Y10BPACK = 1110454617;
        public static final int V4L2_PIX_FMT_PAL8 = 944521552;
        public static final int V4L2_PIX_FMT_UV8 = 540563029;
        public static final int V4L2_PIX_FMT_YUYV = 1448695129;
        public static final int V4L2_PIX_FMT_YYUV = 1448434009;
        public static final int V4L2_PIX_FMT_YVYU = 1431918169;
        public static final int V4L2_PIX_FMT_UYVY = 1498831189;
        public static final int V4L2_PIX_FMT_VYUY = 1498765654;
        public static final int V4L2_PIX_FMT_Y41P = 1345401945;
        public static final int V4L2_PIX_FMT_YUV444 = 875836505;
        public static final int V4L2_PIX_FMT_YUV555 = 1331058009;
        public static final int V4L2_PIX_FMT_YUV565 = 1347835225;
        public static final int V4L2_PIX_FMT_YUV32 = 878073177;
        public static final int V4L2_PIX_FMT_HI240 = 875710792;
        public static final int V4L2_PIX_FMT_HM12 = 842091848;
        public static final int V4L2_PIX_FMT_M420 = 808596557;
        public static final int V4L2_PIX_FMT_NV12 = 842094158;
        public static final int V4L2_PIX_FMT_NV21 = 825382478;
        public static final int V4L2_PIX_FMT_NV16 = 909203022;
        public static final int V4L2_PIX_FMT_NV61 = 825644622;
        public static final int V4L2_PIX_FMT_NV24 = 875714126;
        public static final int V4L2_PIX_FMT_NV42 = 842290766;
        public static final int V4L2_PIX_FMT_NV12M = 842091854;
        public static final int V4L2_PIX_FMT_NV21M = 825380174;
        public static final int V4L2_PIX_FMT_NV16M = 909200718;
        public static final int V4L2_PIX_FMT_NV61M = 825642318;
        public static final int V4L2_PIX_FMT_NV12MT = 842091860;
        public static final int V4L2_PIX_FMT_NV12MT_16X16 = 842091862;
        public static final int V4L2_PIX_FMT_YUV410 = 961959257;
        public static final int V4L2_PIX_FMT_YVU410 = 961893977;
        public static final int V4L2_PIX_FMT_YUV411P = 1345401140;
        public static final int V4L2_PIX_FMT_YUV420 = 842093913;
        public static final int V4L2_PIX_FMT_YVU420 = 842094169;
        public static final int V4L2_PIX_FMT_YUV422P = 1345466932;
        public static final int V4L2_PIX_FMT_YUV420M = 842091865;
        public static final int V4L2_PIX_FMT_YVU420M = 825380185;
        public static final int V4L2_PIX_FMT_YUV422M = 909200729;
        public static final int V4L2_PIX_FMT_YVU422M = 825642329;
        public static final int V4L2_PIX_FMT_YUV444M = 875711833;
        public static final int V4L2_PIX_FMT_YVU444M = 842288473;
        public static final int V4L2_PIX_FMT_SBGGR8 = 825770306;
        public static final int V4L2_PIX_FMT_SGBRG8 = 1196573255;
        public static final int V4L2_PIX_FMT_SGRBG8 = 1195528775;
        public static final int V4L2_PIX_FMT_SRGGB8 = 1111967570;
        public static final int V4L2_PIX_FMT_SBGGR10 = 808535874;
        public static final int V4L2_PIX_FMT_SGBRG10 = 808534599;
        public static final int V4L2_PIX_FMT_SGRBG10 = 808534338;
        public static final int V4L2_PIX_FMT_SRGGB10 = 808535890;
        public static final int V4L2_PIX_FMT_SBGGR10P = 1094795888;
        public static final int V4L2_PIX_FMT_SGBRG10P = 1094797168;
        public static final int V4L2_PIX_FMT_SGRBG10P = 1094805360;
        public static final int V4L2_PIX_FMT_SRGGB10P = 1094799984;
        public static final int V4L2_PIX_FMT_SBGGR10ALAW8 = 943800929;
        public static final int V4L2_PIX_FMT_SGBRG10ALAW8 = 943802209;
        public static final int V4L2_PIX_FMT_SGRBG10ALAW8 = 943810401;
        public static final int V4L2_PIX_FMT_SRGGB10ALAW8 = 943805025;
        public static final int V4L2_PIX_FMT_SBGGR10DPCM8 = 943800930;
        public static final int V4L2_PIX_FMT_SGBRG10DPCM8 = 943802210;
        public static final int V4L2_PIX_FMT_SGRBG10DPCM8 = 808535106;
        public static final int V4L2_PIX_FMT_SRGGB10DPCM8 = 943805026;
        public static final int V4L2_PIX_FMT_SBGGR12 = 842090306;
        public static final int V4L2_PIX_FMT_SGBRG12 = 842089031;
        public static final int V4L2_PIX_FMT_SGRBG12 = 842088770;
        public static final int V4L2_PIX_FMT_SRGGB12 = 842090322;
        public static final int V4L2_PIX_FMT_SBGGR12P = 1128481392;
        public static final int V4L2_PIX_FMT_SGBRG12P = 1128482672;
        public static final int V4L2_PIX_FMT_SGRBG12P = 1128490864;
        public static final int V4L2_PIX_FMT_SRGGB12P = 1128485488;
        public static final int V4L2_PIX_FMT_SBGGR16 = 844257602;
        public static final int V4L2_PIX_FMT_SGBRG16 = 909197895;
        public static final int V4L2_PIX_FMT_SGRBG16 = 909201991;
        public static final int V4L2_PIX_FMT_SRGGB16 = 909199186;
        public static final int V4L2_PIX_FMT_HSV24 = 861295432;
        public static final int V4L2_PIX_FMT_HSV32 = 878072648;
        public static final int V4L2_PIX_FMT_MJPEG = 1196444237;
        public static final int V4L2_PIX_FMT_JPEG = 1195724874;
        public static final int V4L2_PIX_FMT_DV = 1685288548;
        public static final int V4L2_PIX_FMT_MPEG = 1195724877;
        public static final int V4L2_PIX_FMT_H264 = 875967048;
        public static final int V4L2_PIX_FMT_H264_NO_SC = 826496577;
        public static final int V4L2_PIX_FMT_H264_MVC = 875967053;
        public static final int V4L2_PIX_FMT_H263 = 859189832;
        public static final int V4L2_PIX_FMT_MPEG1 = 826757197;
        public static final int V4L2_PIX_FMT_MPEG2 = 843534413;
        public static final int V4L2_PIX_FMT_MPEG4 = 877088845;
        public static final int V4L2_PIX_FMT_XVID = 1145656920;
        public static final int V4L2_PIX_FMT_VC1_ANNEX_G = 1194410838;
        public static final int V4L2_PIX_FMT_VC1_ANNEX_L = 1278296918;
        public static final int V4L2_PIX_FMT_VP8 = 808996950;
        public static final int V4L2_PIX_FMT_VP9 = 809062486;
        public static final int V4L2_PIX_FMT_HEVC = 1129727304;
        public static final int V4L2_PIX_FMT_CPIA1 = 1095323715;
        public static final int V4L2_PIX_FMT_WNVA = 1096175191;
        public static final int V4L2_PIX_FMT_SN9C10X = 808532307;
        public static final int V4L2_PIX_FMT_SN9C20X_I420 = 808597843;
        public static final int V4L2_PIX_FMT_PWC1 = 826496848;
        public static final int V4L2_PIX_FMT_PWC2 = 843274064;
        public static final int V4L2_PIX_FMT_ET61X251 = 892483141;
        public static final int V4L2_PIX_FMT_SPCA501 = 825242963;
        public static final int V4L2_PIX_FMT_SPCA505 = 892351827;
        public static final int V4L2_PIX_FMT_SPCA508 = 942683475;
        public static final int V4L2_PIX_FMT_SPCA561 = 825636179;
        public static final int V4L2_PIX_FMT_PAC207 = 925905488;
        public static final int V4L2_PIX_FMT_MR97310A = 808530765;
        public static final int V4L2_PIX_FMT_JL2005BCD = 808602698;
        public static final int V4L2_PIX_FMT_SN9C2028 = 1481527123;
        public static final int V4L2_PIX_FMT_SQ905C = 1127559225;
        public static final int V4L2_PIX_FMT_PJPG = 1196444240;
        public static final int V4L2_PIX_FMT_OV511 = 825308495;
        public static final int V4L2_PIX_FMT_OV518 = 942749007;
        public static final int V4L2_PIX_FMT_STV0680 = 808990291;
        public static final int V4L2_PIX_FMT_TM6000 = 808865108;
        public static final int V4L2_PIX_FMT_CIT_YYVYUY = 1448364355;
        public static final int V4L2_PIX_FMT_KONICA420 = 1229868875;
        public static final int V4L2_PIX_FMT_JPGL = 1279742026;
        public static final int V4L2_PIX_FMT_SE401 = 825242707;
        public static final int V4L2_PIX_FMT_S5C_UYVY_JPG = 1229141331;
        public static final int V4L2_PIX_FMT_Y8I = 541669465;
        public static final int V4L2_PIX_FMT_Y12I = 1228026201;
        public static final int V4L2_PIX_FMT_Z16 = 540422490;
        public static final int V4L2_PIX_FMT_MT21C = 825381965;
        public static final int V4L2_PIX_FMT_INZI = 1230655049;
        public static final int V4L2_PIX_FMT_IPU3_SBGGR10 = 1647538281;
        public static final int V4L2_PIX_FMT_IPU3_SGBRG10 = 1731424361;
        public static final int V4L2_PIX_FMT_IPU3_SGRBG10 = 1194553449;
        public static final int V4L2_PIX_FMT_IPU3_SRGGB10 = 1915973737;
        public static final int V4L2_SDR_FMT_CU8 = 942691651;
        public static final int V4L2_SDR_FMT_CU16LE = 909202755;
        public static final int V4L2_SDR_FMT_CS8 = 942691139;
        public static final int V4L2_SDR_FMT_CS14LE = 875647811;
        public static final int V4L2_SDR_FMT_RU12LE = 842093906;
        public static final int V4L2_SDR_FMT_PCU16BE = 909198160;
        public static final int V4L2_SDR_FMT_PCU18BE = 942752592;
        public static final int V4L2_SDR_FMT_PCU20BE = 808600400;
        public static final int V4L2_TCH_FMT_DELTA_TD16 = 909198420;
        public static final int V4L2_TCH_FMT_DELTA_TD08 = 942687316;
        public static final int V4L2_TCH_FMT_TU16 = 909202772;
        public static final int V4L2_TCH_FMT_TU08 = 942691668;
        public static final int V4L2_META_FMT_VSP1_HGO = 1213223766;
        public static final int V4L2_META_FMT_VSP1_HGT = 1414550358;
        public static final int V4L2_META_FMT_UVC = 1212372565;

    }

    private static int getMixBufferLength(int imageFormat, int width, int height) {
        switch (imageFormat) {
            case ImageFormat.V4L2_PIX_FMT_NV12:
                return width * height * 3 / 2;
        }
        return 0;
    }

    /**
     * 摄像头路径信息
     */
    private String videoPath;

    /**
     * 底层的摄像头 Fd 信息
     */
    private int videoFd;

    /**
     * 摄像头数据循环读取线程
     */
    private CameraReadThread cameraReadThread = null;

    /**
     * 要显示的 SurfaceView 信息
     */
    private SurfaceView displaySurfaceView;

    public static class DisplayType {
        public static final int ORIGINAL = 0;   // 原始大小，从 Surface 的 左上角开始绘图
        public static final int FIXED = 1;      // 大小缩放，保持图片比例
        public static final int FULL = 2;       // 全图填充
    }

    private int displayType = DisplayType.ORIGINAL;
    private int inSampleSize = 4;

    public void setDisplaySurfaceView(SurfaceView displaySurfaceView) {
        setDisplaySurfaceView(displaySurfaceView, DisplayType.ORIGINAL, 4);
    }

    public void setDisplaySurfaceView(SurfaceView displaySurfaceView, int displayType) {
        setDisplaySurfaceView(displaySurfaceView, displayType, 4);
    }

    public void setDisplaySurfaceView(SurfaceView displaySurfaceView, int displayType, int inSampleSize) {
        this.displaySurfaceView = displaySurfaceView;
        this.displayType = displayType;
        this.inSampleSize = inSampleSize;
    }

    private PreviewCallback previewCallback;

    public void setPreviewCallback(PreviewCallback previewCallback) {
        this.previewCallback = previewCallback;
    }

    /**
     * 摄像头回调接口
     */
    public interface PreviewCallback {

        /**
         * 数据回调方法
         *
         * @param data 视频数据内容
         */
        void onPreviewFrame(byte[] data);
    }

    static {
        System.loadLibrary("V4L2Camera");
    }

    private int videoWidth;
    private int videoHeight;
    private int pixelFormat;

    /**
     * 打开指定路径的Camera，返回 相关文件的 Fd 信息
     *
     * @param videoPath   Camera 路径
     * @param videoWidth  视频宽度
     * @param videoHeight 视频高度
     * @param pixelformat 视频格式
     * @return Fd 信息；失败 返回 -1
     */
    public native int prepareCamera(String videoPath, int videoWidth, int videoHeight, int pixelformat);

    public native int processCamera(int videoFd, byte[] buffer, int length);

    public native int stopCamera(int videoFd);

    public V4L2Camera(String videoPath, int pixelFormat, int videoWidth, int videoHeight) {
        this.videoPath = videoPath;
        this.videoWidth = videoWidth;
        this.videoHeight = videoHeight;
        this.pixelFormat = pixelFormat;
    }

    private boolean isV4L2CameraInited = false;

    /**
     * 初始化摄像头部分
     *
     * @return true，初始化成功；false，失败
     */
    public boolean init() {
        if (isV4L2CameraInited) {
            Log.e("V4L2Camera 已经执行初始化操作，无法再次执行初始化操作");
            return false;
        }
        videoFd = prepareCamera(videoPath, videoWidth, videoHeight, pixelFormat);
        if (videoFd == -1) {
            return false;
        }
        isV4L2CameraInited = true;
        return true;
    }

    public void startPreview() {
        if (isV4L2CameraInited) {
            cameraReadThread = new CameraReadThread();
            cameraReadThread.start();
        }
    }

    public void stopPreview() {
        if (isV4L2CameraInited) {
            try {
                cameraReadThread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            cameraReadThread = null;
        }
    }

    /**
     * 释放摄像头相关部分
     *
     * @return true，释放成功；false，释放失败
     */
    public boolean release() {
        if (!isV4L2CameraInited) {
            Log.e("V4L2Camera 尚未执行初始化操作，执行释放资源失败");
            return false;
        }
        if (stopCamera(videoFd) == 0) {
            isV4L2CameraInited = false;
            return true;
        }
        return false;
    }

    /**
     * 摄像头数据读取线程
     */
    private class CameraReadThread extends Thread {

        private byte[] buffer = null;
        private Bitmap bitmap = null;

        private boolean stopThread;

        public void setStopThread() {
            this.stopThread = true;
        }

        public CameraReadThread() {
            buffer = new byte[getMixBufferLength(pixelFormat, videoWidth, videoHeight)];
        }

        private Bitmap changeSize(Bitmap bitmap, float sx, float sy) {
            Matrix matrix = new Matrix();
            matrix.postScale(sx, sy);
            return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        }

        @Override
        public void run() {

            stopThread = false;

            while (isV4L2CameraInited && !stopThread) {

                if (displaySurfaceView == null && previewCallback == null) {
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    continue;
                }

                int videoDataLength = processCamera(videoFd, buffer, buffer.length);
                if (videoDataLength > 0) {
                    if (displaySurfaceView != null) {
                        BitmapFactory.Options options = new BitmapFactory.Options();
                        options.inSampleSize = inSampleSize;
                        bitmap = BitmapFactory.decodeByteArray(buffer, 0, videoDataLength, options);

                        switch (displayType) {
                            case DisplayType.FIXED:
                                int displayHeight = displaySurfaceView.getLayoutParams().height;
                                int displayWidth = displaySurfaceView.getLayoutParams().width;
                                float heightValue = ((float) displayHeight) / ((float) videoHeight) * inSampleSize;
                                float widthValue = ((float) displayWidth) / ((float) videoWidth) * inSampleSize;
                                if (heightValue > widthValue) {
                                    Bitmap tempChangeBitmap = changeSize(bitmap, widthValue, widthValue);
                                    bitmap.recycle();
                                    bitmap = tempChangeBitmap;
                                } else if (heightValue < widthValue) {
                                    Bitmap tempChangeBitmap = changeSize(bitmap, heightValue, heightValue);
                                    bitmap.recycle();
                                    bitmap = tempChangeBitmap;
                                } else if (heightValue != 1) {
                                    Bitmap tempChangeBitmap = changeSize(bitmap, heightValue, heightValue);
                                    bitmap.recycle();
                                    bitmap = tempChangeBitmap;
                                }
                                break;
                            case DisplayType.ORIGINAL:
                                break;
                            case DisplayType.FULL:
                                int displayHeight_ = displaySurfaceView.getLayoutParams().height;
                                int displayWidth_ = displaySurfaceView.getLayoutParams().width;
                                float heightValue_ = ((float) displayHeight_) / ((float) videoHeight) * inSampleSize;
                                float widthValue_ = ((float) displayWidth_) / ((float) videoWidth) * inSampleSize;
                                if (heightValue_ != 1 || widthValue_ != 1) {
                                    Bitmap tempChangeBitmap = changeSize(bitmap, widthValue_, heightValue_);
                                    bitmap.recycle();
                                    bitmap = tempChangeBitmap;
                                }
                                break;
                        }

                        Canvas canvas = displaySurfaceView.getHolder().lockCanvas();
                        if (canvas != null) {
                            canvas.drawBitmap(bitmap, 0, 0, null);
                            displaySurfaceView.getHolder().unlockCanvasAndPost(canvas);
                        }
                        bitmap.recycle();
                    }
                    try {
                        TimeUnit.MICROSECONDS.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    try {
                        TimeUnit.MICROSECONDS.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        }

    }

}
